# -*- coding: utf-8 -*-
"""
@author: luhx
@file: dd_bs.py
@time: 2023/3/8 9:38
@desc: 顶底新版本业务逻辑
"""
import string
from highlow import algo
from highlow import store
from kds_util.mysql_database import PyMySql
from highlow.dd_obj import BkItem, TBkId, BreedItem, TBreed, HoldItem
from typing import Dict
from highlow.envs import Envs
from highlow.dd_bkinfo import DdBkInfo, DDParam
from kds_util.user_logbook import init_logger as init_logs, user_log as logger, system_log
from highlow.account import DDAccount
from highlow.dd_trader import TraderMan
from util import helper
from highlow.dd_obj import DDParam
from base.kBase import kBase


class DdBs:
    def __init__(self, acc: kBase):
        self.acc = acc
        # self.investor_id = acc.investor_id
        url = self.acc.url
        self.ip = self.acc.ip
        self.mysql = PyMySql(f"mysql+pymysql://root:{Envs.dest_mysql_passwd}@{self.ip}:3306/hqdb?charset=utf8")
        self.bk_info: DdBkInfo = DdBkInfo(acc)
        self.params: Dict[str, DDParam] = None
        self.prev_day_end: bool = False
        self.dd_account = DDAccount(acc, self.ip, acc.investor_id)
        self.trader = TraderMan(acc, self.dd_account, self.bk_info, self.mysql)

    def check_prev_trading_day_end(self):
        """
        判断前一个交易日已经完成
        """
        if self.prev_day_end:
            return self.prev_day_end

        dt = self.params["agL6"].contract_info.Last_Day_Kline.dt

        # if Envs.cur_trade_day == algo.get_kline_trading_day(self.bs.bk_info.pd_days, dt):  # 到达指定交易日，退出
        #     return
        if Envs.prev_trade_day == algo.get_kline_trading_day(self.bk_info.pd_days, dt):
            self.prev_day_end = True
        return self.prev_day_end

    def check_cur_trading_day_end(self):
        """
        判断当前的交易日已经完成
        """
        dt = self.acc.dt
        return Envs.cur_trade_day == algo.get_kline_trading_day(self.bk_info.pd_days, dt)

    def is_kline_white_market(self):
        """
        判断当前系统是否运行到白盘了
        @return:
        """
        try:
            dt = self.acc.dt
            flag = algo.is_kline_dt_white_market(dt)
        except Exception as e:
            print("abc")
        return flag

    def deal_force_stop_loss(self):
        """
        强制止损的处理
        """
        self.dd_account.update_account_status(self.dd_account.contract_opt_positions)
        holds: Dict[TBreed, HoldItem] = self.dd_account.get_holds()  # 先更新一下持仓
        for breed in holds.keys():
            hold_item = holds[breed]
            if hold_item.net_position == 0:
                continue  # 没有仓位的，不用再管
            if breed + "L6" not in self.params:
                continue
            p1: DDParam = self.params[breed + "L6"]
            p2: DDParam = self.params[breed + "L9"]
            if p2.count1m == 0:
                continue
            bk_code = self.bk_info.get_bk_from_breed(breed)
            if not bk_code:
                continue
            if bk_code not in self.bk_info.bk_items:
                logger.error(f"cannot find bk_item from bk_code:{bk_code}!!!")
                continue
            bk_item: BkItem = self.bk_info.bk_items[bk_code]

            if helper.is_forbid_opt_open_position(bk_item):  # 当天买入的不做强损
                continue

            if hold_item.net_position > 0:
                profit = hold_item.long_profit
                profit2 = hold_item.long_today_profit
                volume = abs(hold_item.long_position)
                breed_side = 1
            else:
                profit = hold_item.short_profit
                profit2 = hold_item.short_today_profit
                volume = abs(hold_item.short_position)
                breed_side = -1
            margin = p2.contract_info.one_margin
            money = margin * volume * 0.3
            if money + profit < 0 or money + profit2 < 0:  # 浮动盈亏或是当日盈亏达到保证金的30%进行强制止损
                """如果亏损>保证金的30%,强损"""
                logger.info(f"达到强损线：{bk_code}.{breed}.[{breed_side}],"
                            f"kt:{p2.contract_info.Last_minute_Kline.trade_time},"
                            f"margin:{margin} * volume:{volume} * 0.3 + profit:{profit} < 0 or profit2:{profit2}")
                p1.forced_stop = True
                self.trader.opt_clear_position(hold_item, self.mysql, strategy_type="force_stop", msgs="强损")
                bk_item.trade_type = "CloseOptPosition"  # 保证损完后不再动仓位

    def moving_position(self, params):
        """
        移仓: 巡查当前持仓的所有合约，发现有持仓不在主力合约中且数字比主力合约小，直接平仓，然后取相同数量的主力合约重新开仓
        :return: 无
        """
        if not algo.is_moving_timing(self.acc.dt):
            return
        holds: Dict[TBreed, HoldItem] = self.dd_account.get_holds()
        for breed in holds.keys():
            hold_item: HoldItem = self.dd_account.get_hold(breed)
            if hold_item is None:
                continue
            if hold_item.net_position == 0:
                continue
            if breed + "L6" not in params:
                continue
            p1: DDParam = params[breed + "L6"]  # 取p1的原因是更相信指数的时间而非个票
            p2: DDParam = params[breed + "L9"]
            if p2.count1m == 0:
                continue
            main_contract = p2.contract_info.code
            hdg = int(hold_item.contract.lstrip(string.ascii_letters))
            mdg = int(main_contract.lstrip(string.ascii_letters))
            if hdg < mdg:
                # 先清仓， 再开仓
                logger.info(f"移仓K线时间: {self.acc.trade_time}, {hold_item.contract},pos:{hold_item.long_position}")
                self.trader.opt_clear_position(hold_item, self.mysql)
                bk_code = self.bk_info.get_bk_from_breed(breed)
                if not bk_code:
                    continue
                bk_item = self.bk_info.bk_items[bk_code]
                breed_item: BreedItem = bk_item.components[breed]
                breed_item.contract_id = main_contract
                bk_item.trade_type = ""
                self.trader.opt_open_new_position(self.params, hold_item.net_position, breed, bk_item, holds,
                                                  self.mysql, strategy_type="move_open", remark="移仓开新仓")

    def direct_open_contract_new_position(self, breed: TBreed, side, holds):
        """
        直接开新仓
        :param breed:
        :param side
        :param holds:
        :return:
        """
        bk_code = self.bk_info.get_bk_from_breed(breed)
        if not bk_code:
            return
        bk_item: BkItem = self.bk_info.bk_items[bk_code]

        p2: DDParam = self.params[breed + "L9"]
        contract_id = p2.contract_info.code
        contract_price = p2.contract_info.LastPrice
        position = self.trader.trader_io.dd_account. \
            calc_expected_position(0.8, contract_id, contract_price, side)  # 预期仓位
        self.trader.opt_direct_open_new_position(self.params, position, breed, bk_item, holds, self.mysql)
        return
